3.0 So... You wanna' be a programmer, huh? First things first. Get the proper tools. You will need:
Because these pages have grown 'organic' they sometimes resemble a plate of spaghetti. Well, so does my code :-) If you're looking for the ultimate reference guide, this is the wrong place to be, though the TOC may be of some help. (I use it myself now and again, to be honest.) If you want a downloadable version of this guide get it here. Note that it also may :-) contain a bunch of (hopefully) working .pb files, containing some of the samples you will find throughout these pages. The
online version will be more up to date though! I only update the archives
once I've done a large number of changes.
Setup First install all software and helpfiles and any tools you like. Then come back here. Note: remember, use the tools YOU feel comfortable with. Although I am using certain tools in this primer, that doesn't stop you from using something else... It
looks like the most basic (no pun intended) program in every (programming)
language is the legendary 'hello world' program.
PB Ide Once PureBasic is installed you'll find an icon with the name PureBasic. That's the regular editor for PureBasic. Once you doubleclick it, it's going to look something like this (from 4.xx on Windows XP using the 'classic' theme): On the left side is the main code window, on the right side is a list of all procedures in your code. The button bar at the top provides you with a number of shortcuts. Obviously, the line numbers are there only to provide you an extra visual indication where you are in the code.At first glance, from just looking at the IDE, not much has changed since the early days. You'd be wrong though... Here's the IDE from 5.11 (on Windows 7): I must say it even looks sexier on a Mac, but I have no intention buying one, just for the looks :-)jaPBe jaPBe was my favourite editor until 4.xx. Although jaPBe offers (offered?) some unique functions, I now prefer the regular PB IDE. When you're entering code in the PB editor, they try to recognize what you enter and will show the syntax of the command you are entering at the bottom of the screen. You may have to enable some option to get this to work. (I love it, though I immediately changed the default [Tab] with [Enter].) Enter the following little program: OpenConsole()... and run it by pressing F5 or F6. The editor saves your source and starts the PureBasic compiler, the PB compiler translates this into machinecode and runs it. Of
course, this will compile so fast we won't see a thing. We'll deal with
that later, it's the thought that counts :-)
Here's
my work environment, a few years ago (before I got addicted to multi monitor
setups and started messing around with my SqueezeBox)...
1. You (the programmer) have to provide PureBasic with a place to output any information. In the example above, a 'console' (sort of Dos box) is opened, and the text 'hello world' is printed in there. You could also open a regular Windows window, but you cannot use the PrintN() command there but have to use a different set of commands. So, PrintN() outputs to a console that you first have to open with OpenConsole(). Aha... 2. The sample program contains a bogus loop. It's only there to show an important concept of programming under Windows. As Windows is (more or less) a multi tasking operating system, more than one program runs at the same time. When your program is not doing anything, it's best to give 'processing time' back to the system so other programs can use it. The Delay(1) instruction tells Windows to halt execution for 1 millisec and go and do something else. 3. And last but not least you may notice that (besides this being a very old screenshot of a very old version of PureBasic) I like to have some music in the background whilst working :-) Do the following: in the sourcecode move the cursor to the word 'Delay' and press [F1]. A help on that keyword will pop up. Cool uh? Move the cursor on different keywords and see the results. For PB IDE users: select a block of text, then hit [Ctrl]+[I] and the selected section will be reformatted. For
CodeCaddy
users: move the cursor over a word and hit [Ctrl]+[F1] to look for that
word in your code (depending on the paths you've set up in CodeCaddy).
Try [Alt]+[R] to reformat all (!) your code.
Compiler vs. Interpreter PB is a compiler. It first translates the sourcecode into machinecode before it executes it. This means that errors that the compiler does not spot can cause severe crashes. The debugger helps a little but is not flawless. Also, errors may be reported in the wrong line. (Hey, anybody can say C++?) The advantage of a compiler is, obviously, the final speed of the executable. If
you feel adventurous you can include assembly code directly into your source
code, for that last little speed increase. (Pretty much an advanced topic
that I've dabbled only a little in here.))
Did you notice the and symbols on these pages? gives you the option to skip the next section, which may come in handy for beginners will jump back to the top of the page, where you will find a list of contents It's basic, Jim, but not as we know it. PureBasic looks like basic, smells like basic, feels like basic, some even claim it tastes like basic. It just isn't. Then again, is VisualBasic a basic? Some aspects of the PureBasic syntax may come as a surprise to people that 'know' basic. Pay good attention to the help file. For now, let's keep these things in mind:
A program is nothing more but a list of instructions, telling the computer to do something. These programs are build up using three building blocks:
OpenConsole()And here is what it actually does:
Those Incredible ISzes.. (Ever seen MTV's 'The Max'? No? Go work on your education!) In algebra, the '=' sign means something different than in a programming language like PureBasic. In algebra the '=' means that both expressions left and right of the '=' are equal. Not so in PureBasic, where it depends on the context. When assigning a value to a variable, we 'store' everything right of the '=' character inside the variable. Other programming languages may do things different: a = 5 PureBasicRephrasing the above: put the value of 'five' (5) in the box called 'a'. Of course, later on we want to control the flow of our program so we need to be able to compare two variables, or two expressions. In those cases we are not assigning the right side to the variable name on the left side, but we are COMPARING the right side with the left side, typically a comparison is preceded by the 'If' statement. Different languages do things differently: a = 5 PureBasicRephrasing the above: compare what's left of the '=' character with that on the right side. In PureBasic, this requires the keyword Bool(), If, While, etcetera. PureBasic
knows when it's an assignment and when it's a comparison, as comparisons
only exist in combination with certain (conditional) instructions. Yep.
Bool(),
If,
While,
etcetera.
PureBasic is not C++. The following C++ style of programming DOES NOT work in PureBasic: a = 5*(b=c)If you're that desperate for such a coding style, try this: a = 5* Bool(b=c)Now if you still ask this question in fhe forums then we all know you haven't payed much attention to your homework here...
Newbies For the newbies... a = 1'a' is a variable, a placeholder for a value. In this case, we store the numeric value 1 in it. In the next line, we add 1 to it, so a = a+1 = 1+1 = 2. Then, finally, we print it. The command PrintN() only accepts strings so we first have to convert the numeric value a to a string. (More about strings later...) Think about variables as named boxes containing data. Unfortunately, if you try to run the code above, it will not work. Why? Because it can't output the result anywhere (compare it with the hello world example). To make this code run, we have to add a a few things: OpenConsole()To make it easier to read, I've added some 'empty' lines. All information behind a ';' semicolon is ignored by the compiler. It's a good place to store comments / documentation, and a few sprinkeld semicolon's will certainly make your code easier to read. OpenConsole() ; let's open a console Debug Why start with the Debug command when talking about variables? Simple. You need to understand what Debug dus, and how it is used in the examples that follow... However, some parts in the next section may be confusing if this is your first time around. No problem. Skip the parts you don't understand, continue, then come back later. If neccessary more than once :-) (The full story on the Debugger you'll find here.) The Debug statement can send output to the debugger. Of course, nothing is shown when the debugger is turned off, but you need to pay a little attention, or you could be in for some very hard-to-track-down bugs...
a = 1Debug's behaviour changed with newer versions of PureBasic... ; survival guide 3_2_110 debuggerWell, that's enough delaying and avoiding for now. Let's face the real thing... variables. Variables (aka. placeholders for values) can be of different types. Some are used for integer numbers, some for floating point numbers, some for strings, and all may be of different length. Integers are whole numbers, no fractions. Valid integer numbers are -1, 256, 65536178. Fractions etc. are not allowed, such as 1/3 or 0.447234. PureBasic recognizes a few different types of integers:
(For
the experts, chars .c and pointers
* are also 'integers' .)
In
PureBasic, most integers are signed. This means they can have values ranging
from minus to plus. For example, the PureBasic type 'byte' can hold values
ranging from -128 to +127, whilst in most other languages a 'byte' wll
range from 0 to 255. It's an important difference that we will explore
a little later. Also note that the length of certain types depend on the
'platform' you are using.
Platform: x86 versus x64 The term 'platform' is often used for the kind of processor / Windows your program will run on. The processors in older PC's would only handle 32 bits. Older versions of Windows were only available in 32 bits. Another term used for the older systems is x86, derived from the older CPU names 8086, 80286, 80386, 80486. Summarizing: x86 refers to 32 bits. Newer processors (it all started with the AMD64) can handle 64 bits. You can still run 32 bits versions of Windows on them. If you do, you cannot programs designed for 64 bits. If you run a 64 bits version of Windows you can run both 32 bits and 64 bits software. The term x64 refers to (you guessed it) 64 bits. 64 bits offers more memory space, and is sometimes faster. In
other words: on 32 bits versions of Windows you can only use 32 bits aka.
x86 software. On 64 bits versions of Windows you can use 32 (x86) AND 64
bits (x64) software.
Postfix To specify what type a variable is, you give it a 'postfix', an extra dot plus letter that tells the compiler that that variable is now of that type. ; survival guide 3_3_120 integersOnce you have told the compiler what the type is, you don't have to keep on telling it over and over again. a.l = 1If you don't tell the compiler what type it is, it is automatically assumed to be a .i integer the first time you use it. a.i = 1 ; explicitly tell the compiler a is an .i integerOnce you have told the compiler a variable has a certain type, you cannot change it! a.l = 1This is important to know, as in many basics you can use the same variable name for different types. You cannot in PureBasic. In contrast with some other languages, you can mix and match any variable types in an expression without forcing type: a.l = 1Note that mixing types could have an effect on the outcome! Experts, see here...
Unsigned Integers With 4.40b1 we have two additional types which are unsigned. ; survival guide 3_3_130 unsigned integersSigned and unsigned variables are stored in the same way in memory, it's the way they are interpreted that differs. Confused? You don't have to be. Just keep in mind that PureBasic will take care of the type and the consequences. Use .b if you need to read or store a single memory location and you expect something in the range from -128 to +127, and use .a if you expect something in the range from 0 to +255, that's all there is to it. Remember, a char .c is also unsigned, but its size depends on Unicode mode... In non-Unicode mode a .c equals .a, in Unicode mode a .c equals a .u... (According to the PureBasic developers these are primarily aimed at easier processing of Ascii and Unicode characters in memory, hence the .a which stands for Ascii and refers to 8 bits, as well as the .u which stands for Unicode and refers to 16 bits. Personally, I would have preferred .ub and .uw but I have little to say in the matter :-)) A string
is a bunch of bytes that form together a piece of text. It's one of the
interesting characteristics of any 'Basic' variant. It's also part of your
(?) beach outfit, and the ultimate focus of fanglez' attention. But stick
to its meaning in PureBasic, it's better for your health...
Zero terminated strings Regular PureBasic strings look like this: a.s = "this is a test"To tell the compiler a variable is a string, you have two options at hand: either you use .s or $. There's something special about using $, as the symbol '$' it is considered part of the variable name. Take the following two examples: a.s = "this is a test"Whoops! The above gives out an error. The variable a was first said to be a string, then suddenly changes to a long and that cannot be. The following sample is different: a$ = "this is a test"That will work. Why? Because the variable a$ (the $ is part of the name, remember?) is NOT the same variable as a. Strings can be added together: a.s = "123"Regular strings are zero terminated and thus cannot contain a Chr(0) aka NULL character! Pre 5.50 PuraBasic support Ascii mode and Unicode mode. As of 5.50 strings are always Unicode. More about that later, for now just remember that each character in a string takes up 2 bytes in Unicode. You can (of course) manipulate strings, slice, dice, glue, trim, and more... As of PB 4.00 there is a new type of string, that is allowed to contain zeroes, and that always occupies the same amount of space in memory. That may need a little clarification... A fixed length strings always occupies the same space in memory, no matter what. So... b.s{7} = "this is a test" ; b.s has a length of 7 characters or 14 bytes, so it's contents are clipped to "this is"PB trims all string data that would be too long for the fixed length string to the proper length. b.s{7} = "1" ; now it would only contain 1 character "1" FOLLOWED BY a zero (!)Verify this yourself... (If you use an older pre-5.50 version of PureBasic switch Unicode ON via the menu Compiler / Compiler Options / Create Unicode Executable before running this.) b.s{7} = "this is a test"Fixed length strings work well in structures that need constant length character fields. Fixed length strings don't work well with PokeS() due to the trailing zero that it, euh, 'pokes'... See also here. Update: PokeS() has some optoins now! All string functions still act on encountered zeroes, ie. for all string functions the fixed length string is more or less 'converted' into a normal string before processed, everything after the first encountered zero is simply ignored.
Fixed length strings come in handy if you want or need to store your strings inside structures or specific memory blocks. This makes it easier to deal with certain communication protocols, certain DLL's, or situations where you want to store string information INSIDE a structure itself. Another newcomer since PB v4.00... chars. A char is the smallest part of a string. Sinnce 5.50 a char is always two bytes long and equivalent to an unsigned word. The section on Unicode contains an example of how to create mode independent programs using the .c type. You can force unsigned bytes using the .a type, and unsigned words using the .u type. You can convert strings to integers, and vice verse, using these two commands: a.s = "123" ; a = "123"These come in very handy. Some functions only accept strings, some other functions only accept integers. For example, the PrintN() command from above would only accept a string, so to print out a numeric variable we first convert it to a string before printing. a.l = 123As of 4.20 Val() converts other types as well: a.l = Val("16")See also ValD() and ValF() in the help file, as well as their counterparts StrD() and StrF() and StrU(). Need more conversions? Try the x_val() procedure further down, it does &H &O &0 &B % \ $ and 0X as well. Integers are whole numbers. Sometimes you may need fractional or exponential numbers. For this purpose there are floats. a.f = 1.34567892As floats are, by default, NOT exacting numbers, you may run into surprises when rounding them or using them in certain calculations. However, you may have to use them for certain things. For even higher accuracy, you may use a variant of floats called doubles. a.d = 1.34567892234723974Floats and integers can be freely mixed, but there's one catch: when evaluating an expression or calculation, PureBasic may do some type conversions... see here. a.l = 1As a small note, I'm not entirely sure the PureBasic implementation of mathematics is the best in the world. Oh well. Can't have all, can we? These don't exist in pure, at least not as a variable type. You can use any other integer type for it, and use the following logic: if the result of an expression (or the value of a variable) is zero it's false, if it is not zero then it is true.
You can do all sorts of calculations in PureBasic. Here are some examples: a.f = 1/2 ; divideUse brackets and spaces if expressions get too complex: a.f = ( b/2 ) + ( 22*c ) * ( q/z+12*2-1 )Remember: stuff between brackets has priority. Some special functions: e.l = 10 % 3 ; moduloSee also the help file included with PureBasic [F1] and look for the sections 'general libraries / math' and 'general topics / variables, types and operators'. Use brackets liberally. See below or here for type conversion issues. Stick to the same type throughout and there are no issues. PureBasic's way of evaluating an expression differs from other programming programming languages. Which means? Well, that the outcome of an expression may not be the same as what you would expect. For example... z.f = 0Use the following rules:
In the old days :-) you had to do this to swap two variables: a = 1Now things are better :-) Swap a , bRemember you can only swap variables of the same (non-structured) type. The most often encountered 'bug' is probably the typo... PureBasic can help reducing the number of typos if you start your code with the following keyword: EnableExplicit. When used PureBasic wants you to explicitly define variables, you can't just use them 'on the fly'. Try to run the following program: a = 32This program will run without problems. PureBasic creates a variable 'a' of type Integer (that's the default PureBasic type for untyped variables) and stores '32' in it, then increases it with 1. Now if we would make a typo, and misspell our variable name, we would have a bug. The following program would compile and run perfectly, it's just that the results would not be as expected... a = 32EnableExplicit to the rescue. Once EnableExplicit has been encountered, the compiler will only accept variables that are declared in a Procedure() statement, or are declared using Define, Global, Local etc. Put otherwise, if EnableExplicit was used, the error above would be spotted. The following will not compile: we've told the compiler to check if we defined variables, and we haven't defined 'aa'... ; survival guide 3_3_150 enable explicit... and thus we spot the bug, and correct it: EnableExplicitThe Define keyword has two other uses: it can be used to inform the compiler of an upcoming Procedure(). It can also be used to set the default type for all untyped variables: a = 32 ; default type is long, so it's a.l
Explore the IDE. It has a build-in variable browser that shows you a list of all variables, constants, structures etc. in use... It would be a bit boring if a program would work like a calculator: you start at point a, go to point b, do some calculations on the way and that's it. We want to jump forward, do things conditionally, repeat some other things, and in general enjoy ourselves letting the computer running around in circles. (Sometimes around us...) That can be done. Just like most other programming languages PureBasic contains a number of keywords dealing with flow control. Here's the first one, a conditional statement. If the condition is true, a certain part of the code is executed... a.l = 1 ; this is an assignment, we store 1 in aIn PureBasic it is not allowed to make an evaluation part of an expression! Sorry. PureBasic is not C(++). The following MAY work but is NOT supported. See also here. Just don't do it. (I've been repeating this throughout this Survival Guide, as it is one of the most asked questions on the forum.) a.l = 100 + (b = c) * 100 ; this is NOT supported! If / ElseIf / Else / Endif And what else? Else indeed! a.l = 2And there's ElseIf as well... a.l = 1As may be clear by now, I do not like multiple statements on a line. In several basic dialects you find the following construction: If a = 1 Then b = 1 ; doesn't work in pureBasicIn PureBasic you could do it like this: If a = 1 : b = 1 : EndIf ; works in purebasic but i'm not sure i like itThat line above does the same as: If a = 1 ; the 'en vogue' way of doing it :-) Select / Case / Endselect If we want to test a single variable that can have a few values, we use the Select statement: a = 1Or we could do strings... a.s = "appel"Cool! Starting with PB v4.00 we can do multiple cases and even ranges! a = 1 For / Next Want to repeat something 5 times? For n = 1 to 5ForEach is a special case that only applies to linked lists. PureBasic is different. Which means you may not always be able to do things the 'standard' way (as if there is a standard, hmpf). Anyway, in PureBasic there are two limitations to the For / Next loop that are not that obvious if you come from another Basic variant.
Debug n Next n For n.f = 1.1 to 5.2 Step 0.3 ; not going to workAnd here's how to do it with While / Wend: n.f = 1.1I think For / Next is highly overrated and in many cases While / Wend or Repeat / Until make more sense. Here's a quick and unfair :-) comparison (I know there are other ways to exit a loop):
Repeat / Until Want to repeat something until a condition is met? n = 0A variant of Repeat / Until is Repeat / Forever. Want to execute something if a condition has been met, and continue as long as the condition is being met? n = 0I personally prefer to use While / Wend over Repeat / Until. It tends to keep complex code a little more readable (well, sometimes, not always) but more importantly, I belief it simplifies debugging. With a While / Wend the conditions are set BEFORE executing / entering the loop. But, as usual, it depends. The above will bring us to another important aspect: nesting. We can nest conditions as deep as we like, for example with If commands... a = 1Or for example some For / Next commands... For a = 1 to 5You may want to format your code a little, proper indenting makes it a lot easier to read. Visit the forum for some of the tools that can (re)format your code, or mark the section of your code you want to reformat and press [Alt] + [I]. In many cases While / Wend will do a better job than a For / Next. Which statement to use where is more of a mindset than a language implementation. If you need a quick and dirty and unconditional loop use a For / Next. Eventually with a Step. I use For / Next to quickly test something, or for running (small) unconditional loops. b.l = 0One of the statements I've pretty much never ever use is Break. It's a mindset thing just like Step. If you can live without it your coding life may become easier. It may also NOT become any easier, after all it's your own choice :-) Try to rewrite the above to break out of the loop when b.l goes over 100: b.l = 1And compare it with this: b.l = 1The reason why I don't like Break is that it's sometimes unclear what the Break belongs to. At least to me it is :-) It's your call. There's one situation, however, where you absolutely need Break: when doing a Repeat / Forever. Normally, we 'assign' a value to a variable, as in: a.l = 1When assigning, you'll only find a single variable name left of the '=' sign, while on the right side there is a (complex) calculation or 'expression'. When using conditional statements, we are 'evaluating' an 'expression'. In other words, we check if something is true, or not true. In some more other words, we compare the 'expression' left of the '=' sign with the expression right of the '=' sign. a.l = 1In these 'conditional' cases, left and right of the '=' sign there can be a full (complex) expression: a.l = 1We could check if things are the same, but also if they're not, or if they're larger or smaller: ; survival guide 3_4_150 evaluationsPureBasic is not C, so assigning the result of an evaluation to a variable is not allowed, see also the Bool() command. a = (b <> c) ; this is soooo wrong 3.5 Procedures and variable scope Defining procedures. Get ready for one of the most essential parts of PureBasic: procedures. A procedure is a small block of code that does something, can be called with parameters, and might even return a result to us. It allows us to reuse parts of our code, and keeps things organized and readable. Reasons enough to use them then :-) Here's
sample code: (I've added line numbers to explain how things work, don't
copy them if you want to try out this code.)
Now, what does it do? From top to bottom... 1... first, we tell the compiler we're going to create a procedure with the name 'sample'...What will happen when we run the code above? Nothing. Why
not? Because we have told PureBasic we have a procedure, but we never call
it. It sits there ready for us, but we don't use it. We forgot to 'call'
our procedure
Calling procedures Here's how we call the procedure: Procedure sample(a.l)Check that last line. It's not a regular keyword such as PrintN() so PureBasic knows it has to be a procedure. PureBasic looks back to see if it already saw this name, and if so it will call it with the given parameter. You can use as many parameters as you like: Procedure sample3(a.l,b.l,c.l,d.l,e.l)Obviously, we can reuse the procedure as many times as we like. Procedure sample(a.l)In the code above we call the procedure 5 times. As you can see, we can either pass values (as '1') or variable names (as 'n') as parameters. We also notice something else: inside the procedure we use a different name than inside our main code. Is this correct? We didn't get an error... In fact, yes. This is correct. These are local variables, that only exist inside the procedure. More on that a little later, first we deal with a great feature that arrived with PureBasic v4.00... optional procedure parameters! Yes!
In the procedure definition you normally specify how many parameters of what type are expected. For example: Procedure sample(a.l,b.l,c.l)Starting with PB v4.00 we now can assign default values to each parameter, and this effectively also means they have become optional! Procedure sample(a.l,b.l,c.l,d.l=4) Local vs. global All
variable names WITHIN a procedure are considered local UNLESS we have told
the compiler otherwise. That is, variables only exist within the procedure.
They will not affect any variables with the same name defined elsewhere.
A sample makes this even more clear.
Let's step this through... 1... we start by telling the compiler variable c is a global variable, that is it exists inside all procedures everywhereWhat we will notice is that c has changed after calling the procedure whilst a and b still have their original value. Why? Because the variables a and b INSIDE the procedure have nothing to do with the variables outside the procedure. They are local. You can create, change and destroy them at wish within the procedure as they are effectively different variables. Then, when we exit the procedure, the original a and b are restored to their old values. And c? Well, we told the compiler it was a global variable. So if something inside the procedure does something to c that will show outside the procedure as well. One
more example:
Let's break this one down as well... 1... we start by defining a string variable called 'a' and fill it...When we run it the following will happen... 1... a.s is filled with the text...You see, the string variable a.s only exists outside the procedure whilst a.l only exists within. They're both local, so the compiler does not generate an error. It would if, for example, they would both exist inside the same procedure, as then we would try to change the type of a variable (a variable cannot be a string and an integer at the same time, it's not a politician :-)). Don't forget: you have to declare a variable as global before using it at all, you can't change something later down the line from local to global... (For the experts: PureBasic is a single pass compiler.) As of PB 4.xx arrays and lists defined inside the procedure itself are considered local and protected within the procedure.
There's more to local and global variables, you can also use the Protected, Static and Shared keywords. A short list:
Global b = 5 Protected (Also known as 'local' in other languages.) A 'protected' variable is a local variable that will not exist outside the procedure, nor will it be affected if another variable with the same name is declared global elsewhere. Every time the instruction is encountered in combination with an assignment, the variable will change value, ie. Protected a.l ; doesn't change the value of a.lLocal (protected) variables are, when not otherwise specified so, set to zero upon creation. A 'global' variable can be changed from anywhere in the program, except under the following conditions:
Global a.l ; doesn't change the value of a.lIf you want each thread to have its own global vairables use the Threaded keyword. If a variable is declared ouside the procedures and not declared global, a procedure can access it by declaring it shared from within the procedure. a.l = 5I would advise against using this keyword. It's a good starting point for hard to find and fix bugs... Static is a very interesting variant that only since PB v4.00 makes much sense... Ahum. That's not totally honest, let's say it now makes more sense :-) Static variables exist only within the procedure, and not outside. However, upon every execution they retain their previous value. Code shows this better: Procedure test()When the procedure is first time ever run, it will assign 1 to a.l, if ever executed again the line Static a.l = 1 wll no longer affect the value of the variable. Try the code above, it will output 2 and 3. Why
not use globals in those cases where you want to 'remember' certain values
inside your procedure? Because the static variable is 'invisible' the the
rest of your program, it only exists inside your procedure, and because
it is intialised upon the first call of the procedure.
Nesting Variables
are always local to the procedure they are defined in. Here's one more
example, and this time we nest the procedure calls...
1... nothing happens here, all procedure definitions...Although not explicitly mentioned, the above shows another very important aspect... the correct sequence of defining and calling. YOU HAVE TO DEFINE SOMETHING BEFORE YOU CAN USE IT. If you want to use a procedure in your code, you have to define it first before you can call it later. Logical, isn't it? You cannot only pass a value to a procedure, a procedure can pass a value back as well. A simple example shows it best: Procedure.l up(z.l)You can see that we added a 'postfix' to the keyword Procedure. What type of variable the procedure returns is defined this way, just like we define a regular variable type. To return a long integer we use Procedure.l, for strings we use Procedure.s etcetera. Actually passing back the variable is done using the ProcedureReturn statement. In the example above we return the value of 'z'. In contrast with some other languages, the statement ProcedureReturn may exist anywhere in the procedure, and will immediately terminate the execution of the procedure and 'empties' any stuff on the stack... Forget about those terms, here's a good example: ; survival guide 3_5_150 returning valuesBoth procedures are correct and valid and will work.
Ouch. We haven't touched arrays or lists yet... Well, I could move this section (but I've never been too organized myself :-)) so I'll just give you the opions...
However, be aware, you are not passing the full contents of the array to the procedure, but you are telling PureBasic which array to use. In other words, any change you make to the array from inside the procedure is affecting the array outside! Another way to think of this is that you're using a different name for the same array inside the procedure. This is called 'by reference'. The example below shows using arrays and lists as parameters in procedure calls. Note that as of 4.30 (or earlier, I might have missed this in earlier incarnations of the Survival Guide) you have to add some information to the procedure definition and the procedure call.
; survival guide 3_5_200 passing arrays maps listsWhen passing an array as a parameter, you have to specify the number of indices. In the example above the array had one dimension, so in the procedure definition it says '1'. For example: Dim a(10) 3.6 Constants With all that easy stuff behind us, it's time to add some more serious elements to the mix. First constants. A constant is a variable that cannot change, hence the name constant :-) (There's another difference: a constant is used 'by value', that is every time you use a constant, the compiler is actually inserting the value of the constant in the code, while when you use a variable, it is using the location where the variable is stored and takes the value from that location. But... forget it, just remember that constants are things that never change once you defined them.) a.l = #True ; which is by default 1You cannot change a constant and you don't have to define its type. If you try to change a constant, the compiler will give an error. Constants are often used in combination with (external) libraries, and can store parameters, options, etc. In PureBasic there are many constants already pre-defined, for PureBasic itself as well as for Windows. (See also here.) PureBasic has a great feature that helps you with 'automatic increasing numbers' for constants called Enumeration.
No. We're not going to do anything difficult yet. You'll have to wait for stuff with pointers and structures... We're only going to touch some small things, don't worry if you get it all now, we'll get back to this when it gets more important. (If you want you can skip this paragraph for now and come back to it later.) First, the warning: WHEN YOU READ FROM OR WRITE TO THE WRONG PLACE IN MEMORY, YOUR PROGRAM AND / OR COMPUTER MAY CRASH. SO BE CAREFUL. That's why Windows XP and everything later are better platforms to work on... Even if a program crashes, the whole machine may stay up and running (not always though)... Under Windows 98 you had to reboot a lot... (C++ adepts and Basic haters: peek and poke have nothing to do with bad programming, they are just a way of writing to and reading from memory. Although I'd personally wouldn't mind a different syntax or some alternatives... Then again, you can always use pointers :-)) First some code: a.s = "test" ; a is a stringWe start creating a string called a. Then we find the place where the string is located in memory using the '@' sign. We then overwrite that part of memory with new information and finally display it. Some more code, now reading directly from memory... a.s = "test" ; a$ is a stringWe create a string, then display what's at its location in memory. PokeS(), PeekS() etc. automatically adjust to the Unicode mode we're in, unless we specify how to they should operate by adding a specific flag. (Check here for more details.) You can always check how many bytes a string actually occupies (excluding any trailing zeroes, we're typically talking about zero terminated strings) using StringByteLength(). (There's also fixed length strings, see here.) So far, so good. There are a few things with strings we have to be careful of... PureBasic uses something called 'zero terminated strings'. That means a string (in memory) always ends with a zero. So when you tell PureBasic that a string is 4 characters long and contains "test", it is stored into memory as TEN bytes, each of the four characters takes 2 bytes (totalling 8) plus another two bytes for the 'terminating zero'. Note that, in Unicode, each character occupies TWO bytes.(More on strings in memory.) Normally you won't notice. When you're dealing with PeekS() and PokeS(), however, you need to pay some attention. PeekS reads data from the memory and returns it in a string. It keeps on reading until it hits a zero, unless you specify a maximal length. a.s = "test" ; a$ is a stringPokeS is the risky sibling to PeekS. It writes a string into memory. You can specify a length, BUT... it will always write the terminating zero! ; survival guide 3_7_110 zero terminated strings Bytes, words, longs, quads... For each of these, PureBasic has a read and write variant. With the @ character we indicate the address of a variable, the place where it is stored in memory. ; survival guide 3_7_111 types peek and pokeNote that on 64 bit platforms @ will return a 64 bit value! So the following would work on a 32 bit machine, but not on a 64 bit machine! a.l = 1The proper way to do it is like this: a.l = 1 Why and how... (For those who care...) The smalles thing a computer can handle is a bit. A bit is a 1 or a 0. That's it. Eight of those bits together form a 'byte'. A byte can store any number from 0 to 255. A computer's memory is made up from thousands or millions of those bytes. (1024 bytes is called a kilobyte, 1048576 bytes is called a megabyte.) There are eight bits in a byte, and they are numbered 0 to 7 from right to left: bit 7 - if set to one equals 128So, a single memory location is called a byte and can contain 8 bits. Two memory locations together are called a word and can contain 16 bits, etcetera. For example, the computer stores the number 132 like this: 10000100, which means 1 x 128 + 0 x 64 + 0 x 32 + 0 x 16 +0 x 8 + 1 x 4 + 0 x 2 + 0 x 1 = 132. Two bytes together are called a 'word'. All bits in a word are numbered from 0 to 15, with bits 0 to 7 in the first byte, and bits 8 to 15 in the second. Four bytes together are called a 'long'. The first byte contains bits 0 to 7, the second 8 to 15, the third 16 to 23, the fourth 24 to 31, for a total of 32 bits. Eight bytes together are called a 'quad'. The first byte contains bits 0 to 7, the cecond bits 8 to 15, etc. etc.
Binary numbers Read the part of bits above? Right. You could build up a byte with for example bits 5 and 6 set to one using this apporach: b.b = 64+32Or you could simply do it this way: b.b = %01100000If you know about hexadecimal then this would do the job as well: b.b = $60To set, for example, bit number 2 of an existing byte, try this: b.b = %01100000Or to clear bits 5 and 6: b.b = %01100000Look here for more on binary operators... We've already touched upon the subject of signed and unsigned variable types. Just to keep in mind: both types are stored in memory the same way, they're just interpreted differently. This little piece of code shows that %11111100 means -4 in signed bytes, and 252 in unsigned bytes. (The % character indicates it's a binary number, see below.) unsigned.b = %11111100The way signed and unsigned are stored is the same, the way they are interpreted is different. This depends on the variable type. An Intel based system (anything called a 'PC' that runs Dos or Windows) stores information in memory in 'little endian' format, the lower bytes (the ones that contain the lower bits) go first, followed by the higher bytes. Some other CPU's (for example Motorola's 68000) use 'big endian' format, where the higher bytes go first. For now just forget the term, simply remember that information is stored in 'low to high' order on your Windows box: ; survival guide 3_7_200 little endian signed bytesYou may notice that reading the individual bytes will return some NEGATIVE numbers. This is because the PureBasic type .b is a signed type. When using unsigned numbers a byte can hold anything from 0 to 255, when using signed numbers a byte can hold anything from -128 to +127. Actually the memory itself doesn't hold different information, it's just our way of interpreting it. (Hmmm. Must have said that before a couple of times...) There is a way to fix that, using a binary AND with &$FF, which is shown in the last line... (Don't worry, more on binary And is coming up later...) The good news is that as of 4.40b1 we now can read unsigned stuff as well. The above, now using unsigned bytes, may be easier to read (no more ugly &$FF parts)... ; survival guide 3_7_201 little endian unsigned bytes
A number is a number, stored as a few bits (and bytes) in memory. Sometimes you might need a number as part of a string before displaying it. Or you get information from another source and you need to convert a string (aka text) to a number... a.s = "17521"Computers are good at sequences of zeroes and ones. We poor humans are not. We could write down every binary number in its components, but to write, for example, 17521 as 00000000000000000100010001110001 (that's binary or base 2) is somewhat... cumbersome... Fortunately, there are alternatives, such as hexadecimal (base 16). a.l = 17521 Hex(), Bin() and x_val() PureBasic has some built-in functionality that can help us here. On compilation time, the compiler can translate binary and hexadecimal numbers in the source code automatically if we precede them with a '%' or a '$' symbol. During run-time we can turn any value into a string, either decimal, hexadecimal or binary, using Str(), Hex() and Bin(). Unfortunately, there's only Val() for decimal numbers for the reverse direction. Ooops. Strike that. As of 4.20 Val() now supports binary and hex as well. Still, here's a little help for other cases, and pre 4.20... ; survival guide 3_8_110 hex bin valIf you want to test the above, you can use the following code:: a.l = 1234 3.9 Boolean and binary First an overview of some 'logical' 'binary' ''boolean' functions... A B AND OR XOR Boolean logic: And When a few conditions apply, for example a must be one and b must be one, we could code it like this: a.l = 1But we could combine these on a single line, using the And keyword: a.l = 1 Boolean logic: Or How about the following condition? If a is one, or b is one, or both are one? You guessed it... a = 1 Brackets When creating more complex logic, we can use brackets to group conditions together to create lines such as: If ( ( a=1 Or b=1 ) And ( c=1 Or d=1 ) ) And e = 1Always add brackets when code might be interpreted in different ways... Compare the following three lines: If a=1 Or b=2 And c=3What was the programmer's intention? The intention is clear in the second and third line, but the first line just might cause some confusion. And even though PureBasic follows a certain logic when evaluation such a combined expression it just might be wise to add a bunch of brackets, if only to be able to read code ten years later... Test variables against zero When creating more complex logic, we can use brackets to group conditions together to create lines such as: If ( ( a=1 Or b=1 ) And ( c=1 Or d=1 ) ) And e=1(Euh, I think I just repeated myself... must be my old age...) While we're at it, there is a way to make our code shorter. If we don't feed the If statement a real evaluation, such as a = 1, but instead give it only the variable it will still work! Why? Because what If and other statements do is this: they check if the result of an expression is zero or not... If it's 0 it's false, otherwise it's true. A little piece of code shows it best: a = 0So, anything zero is false, anything else is true. This will come in very handy with some of the PureBasic commands. You see, a large number of them return 0 (when they fail) or something else (when they succeed). If we take the more complex logic from a few lines back, we might be able to rewrite it as: If ( ( a Or b ) And ( c Or d ) ) And eIf the result of a test is zero it means it's false, if the result of a test is non-zero it's true Here's one more example: a.l = #True ; which is by default 1 Boolean logic: the case of the missing Not... ... does not apply anymore! Finally! Looks like we finally got this little keyword that makes our life a little easier... 'Not' simply turns #True into its opposite #False. Procedure check_everything()We could also write it this way: (for the Not haters :-)) Procedure check_everything()Tip: make sure you sprinkle enough brackets to keep your code readable... and if expressions get too long, break them up in parts, but make sure you understand the impact of type conversion. a = (a = b) does not work. Don't even try or ask for it. Don't. Please. Don't. Ask. Ever. PureBasic is not C or C++. It just won't work. Don't ask for it. Use Bool(). If you're in desperate need for constructions like a = (a = b) then Purebasic 5.11 now provides the Bool() keyword: OpenConsole() Binary operators The logic above can also be applied to bytes. A sample goes a long way... a.l = %11110000You can compare this with the little table at the beginning of this section... Now, as we are dealing with binary operators, let's address the shift instruction right now: a.l = %11110000Binary operators are often used to combine 'flags' for other parts of the program. You can also use them to test if bits have been set or reset: #flag1 = %00000001 ; $01 or decimal 1
Boolean
is used in combination with conditions (If) and bitwise is used to operate
on variables.
Some examples: a.a = %00000011 & %00000110 ; %00000010 3.10 Arrays (It's late but I want to get this over with...) The (almost) final section of Primer I... arrays. Arrays are... easy :-) Think of arrays as tables. Tables where every box of the table can contain a value. We can have tables of one dimension (like a list), two dimensions (a regular table) or even more than two dimensions (a book with on every page a table, a bookcase with books with on every page a table, a library with bookcases, a city with libraries)... Sample: Dim x.l(10) ; this is a one dimensional table of 11 longsArrays allow us to store and organize larger numbers of similar data. Before we can use an array we first have to dimension it. An array always starts with 0, so Dim x.l(10) creates a list of 11 positions, from x.l(0) to x.l(10). It is possible to redimension existing arrays and keeping the data they contain using the ReDim() command. There is one limitation: you can only redimension the last indice. Dim x.l(10,10)In short:
3.11 Using strings Ha! And I thought I would keep this page alone! And you thought you were done! Ha! Again! :-) Actually, I was updating the TOC, and I just realized there's something missing: a section on strings, ore more precisely the manipulation of strings. So there ya' go... Strings are, you guessed it, not the basic building blocks of the universe (though Stephen Hawking may disagree :-) nor are they the kind of adult underwear the other half would get excited over... (I tried to insert a funny link to www.strings.com or www.underwear.com but neither worked...) Strings are variables that we use to store text. There are many different flavours of strings (zero terminated strings, fixed length strings, Unicode etc) but if we stick to the regular PureBasic commands (and don't care much how strings are stored in memory) then we're lucky... PureBasic will take care of all the tricky stuff and we just slice and dice to our hearts delight. (It's late and there's a cooking program on the television, you can tell.) Let's start with regular string manipulation of regular strings. It doesn't matter much if they are Unicode or not. a.s = "12"As we can see it's possible to 'add' two strings together. We can also find out it's length (in characters), and we can retrieve some characters from the left, the right, or the middle of the string. The command Space(10) creates a string of 10 spaces (I bet you didn't see that one coming). If we have a string with some unwanted spaces left or right, we can 'trim' these spaces. a.s = Space(10) + "abcABC" + Space(10) Change is the name of the game We can easily change the case of the string to upper- or lowercase. Note that you cannot change the case of special characters. If it's not part of the regular (english) characterset, it's a no-no. a.s = "ABC abc ABC abc"The ReplaceString() function allows us to replace part of a string with another string. Unless you use the #PB_String_InPlace flag the replacement doesn't have to be the same length. You could replace part of a string with nothing, and PureBasic even has a command for that: RemoveString(). The LSet() and RSet() commands allow some 'formatting'. For example, if we have a integer '456' that we want to turn into a 6 digit number with leading zeroes, we could do something like this: n.l = 456The (in other Basic languages) very common String$() can be replaced like this: a.s = Rset("",6,"-") And I still haven't found... FindString() allows you to find one string inside another: a.s = "abcdefghi"Unfortunately there is no native RFindString() in PureBasic, but here is a replacement (not the fastest way, but it works): Procedure.l x_rfindstring(string.s,find.s)A command that finds-and-slice'n'dices-all-in-one is StringField(). StringField() allows you to take strings to pieces, using a given 'seperator'. For example: Debug StringField("This is a test",1," ")... would result in 'This'. In the example above StringField() slices the strang on every occurence of the seperator (which is in this case a space). Now the first element found is 'This', to find the second element we increase the index: Debug StringField("This is a test",2," ")
... should especially check out the following subjects, if they haven't already :-)
When you feel familiar enough with PureBasic you may continue with Primer II... (I strongly suggest it :-)) For
any questions related to programming in PureBasic do not ask me
but visit the forum!
(There you will find people who are much more knowledgable about PureBasic
than I am. I admit that's rather easy :-))
|